Ritorna all'inizio


Domini funzionali e cartesiani

Un dominio è, nella pratica della semantica denotazionale, una CPO: un ordine parziale completo dotato di elemento minimo . Questo articolo dà per acquisita la teoria degli ordini, poset, catene e lub, CPO e CPO, funzioni monotone e continue, Teorema di Kleene, e si concentra su un'altra domanda: come si costruiscono i domini in modo compositivo, e perché le funzioni che vi operano risultano continue.

La motivazione è dare semantica denotazionale a un linguaggio funzionale tipato (l'esempio di riferimento è HOFL, Higher-Order Functional Language). Servono allora non un dominio, ma una famiglia intera: un dominio Dτ per ciascun tipo

τ::=intτ0*τ1τ0τ1

in modo che ogni termine t:τ denoti un valore [[t]]ρDτ (dove ρ è un ambiente type-sensitive che assegna alle variabili valori del dominio giusto). Inoltre, poiché HOFL ha la ricorsione recx.t, il significato si ottiene risolvendo un'equazione ricorsiva tramite fix, e quindi ogni Dτ deve essere una CPO e ogni funzionale coinvolto deve essere continuo.

La strategia è compositiva: si fissa il dominio base Dint e si forniscono dei costruttori che, dati Dτ0 e Dτ1, producono Dτ0*τ1 e Dτ0τ1. Il requisito cruciale è che ogni costruttore preservi la struttura di CPO, e che le operazioni associate (proiezioni, applicazione, ricorsione…) siano continue.

Il dominio piatto degli interi

Il tipo base si interpreta nel dominio piatto Dint=: gli interi più un che rappresenta la non terminazione. Essendo un ordine piatto è automaticamente una CPO (ha bottom e ammette solo catene finite).

Le operazioni aritmetiche op{+,×} si sollevano al dominio piatto come estensioni strette op:×: il risultato v1mathbinopv2 vale v1mathbinopv2 quando entrambi gli argomenti sono interi (v1,v2), e vale altrimenti (cioè non appena v1= oppure v2=).

"Stretta" significa che si propaga: se un argomento diverge, diverge il risultato. Queste funzioni sono monotone e continue, automaticamente, poiché il loro dominio × ammette solo catene finite (vale il lemma "niente catene infinite monotona = continua").

Prodotto cartesiano di domini

Date due CPO D ed E, il prodotto cartesiano D×E si ordina componente per componente:

(d0,e0)D×E(d1,e1)d0Dd1e0Ee1

Si verifica che D×E è ancora una CPO:

- è un poset: riflessività, antisimmetria e transitività si ereditano da quelle di D ed E;

- ha bottom D×E=(D,E), perché (D,E)(d,e) per ogni coppia;

- è completo: il lub di una catena {(di,ei)} si calcola componente per componente,

i(di,ei)=(idi,iei)

È un maggiorante (ogni componente lo è) ed è il minimo dei maggioranti (se (d,e) maggiora la catena, allora d maggiora {di} ed e maggiora {ei}, da cui la disuguaglianza componente per componente).

Le proiezioni π1(d,e)=d e π2(d,e)=e sono monotone e continue. La continuità di π1, ad esempio, è immediata dalla definizione di lub di coppie:

π1(i(di,ei))=π1(idi,iei)=idi=iπ1(di,ei)

Variante: prodotto smashed. Talvolta si usa il prodotto "schiacciato" DE, che identifica in un unico bottom tutte le coppie con almeno una componente indefinita. È un buon esercizio verificare che anch'esso è una CPO.

Switch lemma

Prima di affrontare lo spazio delle funzioni serve uno strumento tecnico, tanto semplice da enunciare quanto utile: lo switch lemma, che dice quando è lecito scambiare l'ordine di due lub annidati.

Lemma (switch). Sia E una CPO e {en,m}n,m una famiglia doppiamente indicizzata, monotona in entrambi gli indici:

en,men,msennmm

Allora ogni riga, ogni colonna e la diagonale sono catene, e i tre modi di "saturare" la matrice coincidono:

nmen,m=mnen,m=kek,k

Idea della dimostrazione. Si prova che tutti questi insiemi hanno lo stesso insieme di maggioranti (per riga: ogni elemento di una riga sta sotto al lub di riga; per diagonale: ogni en,m sta sotto a ek,k con k=max{n,m}, perché nk e mk). Avendo gli stessi maggioranti, hanno lo stesso minimo dei maggioranti.

Intuitivamente: per calcolare il limite di una "matrice crescente" si può procedere per righe, per colonne o lungo la diagonale, ottenendo sempre lo stesso risultato.

Spazio delle funzioni

Date due CPO D ed E, il dominio funzionale [DE] è l'insieme delle funzioni continue da D a E, non tutte le funzioni, solo quelle continue, ordinate punto a punto:

f[DE]gdD.f(d)Eg(d)

Conviene avere in mente alcuni esempi su []. Due funzioni totali distinte sono sempre inconfrontabili (ogni funzione totale è massimale): per avere fg con fg occorre che f sia indefinita () là dove g è definita, e che coincidano altrove. L'ordine [DE] misura quindi quanto una funzione "raffina" l'altra estendendone il dominio di definizione.

Anche [DE] è una CPO:

- è un poset, con relazione ereditata punto a punto da E;

- ha bottom [DE]=λd.E, la funzione ovunque indefinita;

- è completo, e questo richiede due passi.

1. Ogni catena di funzioni {fn} (anche non continue) ha come lub la funzione definita punto a punto

h=λd.nfn(d)cioèh(d)=nfn(d)

che è un maggiorante ed è il minimo dei maggioranti (entrambe le verifiche si fanno punto a punto).

2. Se le fn sono continue, allora anche h lo è, ed è qui che interviene lo switch lemma. Data una catena {di} in D:

h(idi)=nfn(idi)=nifn(di)=infn(di)=ih(di)

Lo scambio centrale dei due lub è lecito perché la famiglia en,i=fn(di) è monotona in entrambi gli indici: se nm e ij allora fn(di)fn(dj)fm(dj) (per continuità/monotonia di fn e per fnfm).

Poiché il limite di una catena di funzioni continue è continuo, esso appartiene a [DE], che risulta dunque chiuso e completo. Ricordiamo infine che la composizione di funzioni continue è continua, cioè se f[DE] e g[EF] allora gf[DF].

Domini sollevati e somma disgiunta

Per completare la cassetta degli attrezzi servono altri due costruttori.

Il sollevamento (lifting) di una CPO D è D={}D. Attenzione: non è l'ordine piatto. Il lifting aggiunge un nuovo bottom strettamente sotto a una copia fedele d di D, conservandone l'ordine interno:

Dxx;d1Dd2d1Dd2

È una CPO, con idi=idi. L'operatore di lifting ()*:[DE][DE] trasforma f in f* con f*()=E e f*(d)=f(d): è ben definito, monotono e continuo. Su di esso si basa la comoda notazione di de-lifting letxt.e, che estrae il valore da t (se definito) e lo assegna a x in e, propagando altrimenti E.

La somma disgiunta D+E=(DE,) affianca i due domini tenendoli separati (tramite iniezioni continue ιD,ιE); serve a interpretare i tipi unione. La costruzione dell'ordine, del bottom e delle iniezioni è un utile esercizio.

Teoremi di continuità

Verificare la continuità "a mano" di ogni funzione semantica sarebbe poco pratico. I teoremi di continuità permettono di ridurre il problema a controlli più semplici, e forniscono un compendio di operatori già noti essere continui.

Continuità sul codominio prodotto

Teorema. Sia f:DE1×E2 e poniamo gi=πif. Allora f è continua se e solo se g1 e g2 lo sono.

La direzione () segue dal fatto che le proiezioni sono continue e la composizione di funzioni continue è continua. La direzione () si calcola componente per componente, usando che f(d)=(g1(d),g2(d)) e che il lub nelle coppie è componente per componente. In pratica: una funzione a valori in un prodotto è continua esattamente quando lo sono le sue componenti.

Continuità separata sugli argomenti

Teorema. Sia f:D×EF. Definite le applicazioni parziali

fd=λe.f(d,e):EFfe=λd.f(d,e):DF

allora f è continua se e solo se fd è continua per ogni dD e fe è continua per ogni eE.

Questo è uno dei risultati più usati: per stabilire la continuità di una funzione a due argomenti basta controllarla un argomento alla volta. La direzione delicata è (): data una catena {(dk,ek)} in D×E, si separa il lub di coppie e si applica la continuità prima su un argomento e poi sull'altro, usando infine lo switch lemma per ricomporre il doppio lub

f(k(dk,ek))=jif(di,ej)=kf(dk,ek)

applicabile perché f(di,ej) è monotona in i e in j (le applicazioni parziali sono monotone).

Attenzione. Continuità (e perfino monotonia) separata va verificata con cura: una funzione può essere ben definita "come grafo" ma non monotona se "guarda" se un argomento è . Ad esempio g(x,y)=xy se x,y e g(,y)=0 non è nemmeno monotona, perché g(,)=0not valori prodotti quando gli argomenti si raffinano.

Operatori fondamentali

I teoremi precedenti permettono di dimostrare che i costituenti della semantica denotazionale sono continui:

- Applicazione apply:[DE]×DE, con apply(f,d)=f(d), è continua (la si verifica separatamente nei due argomenti: fissato f è continua perché f lo è; fissato d è continua per la definizione di lub punto a punto nel dominio funzionale). Dunque apply[[DE]×DE].

- Punto fisso fix:[DD]D, con fix=λf.nfn(D), è esso stesso continuo. La dimostrazione mostra per induzione su n che ogni λf.fn(D) è continua (caso base: funzione costante λf.D; passo induttivo: usa lo switch lemma), e poi conclude che fix è continua perché lub di funzioni continue. Questo è esattamente ciò che rende ben definita e compositiva la denotazione di recx.t.

- Currying curry:[D×EF][D[EF]] e il suo inverso uncurry preservano la continuità e sono mutuamente inversi: stabiliscono l'isomorfismo [D×EF][D[EF]], la versione "su domini" della corrispondenza tra funzioni a due argomenti e funzioni che restituiscono funzioni.

In definitiva, i costruttori (prodotto, spazio funzionale, lifting, somma) sono chiusi rispetto alla struttura di CPO, e gli operatori fondamentali sono continui: abbiamo così un linguaggio completo per costruire, in modo compositivo, i domini e le funzioni semantiche di un linguaggio funzionale di ordine superiore.

Articoli correlati